package org.example.nebula.common.utils;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author: jszhang
 * @date: 2022/12/27-13:38
 */
@Slf4j
public class NebulaUtil {

    static final String NEWLINE = "\r\n";
    static final Pattern PARENTHESIS_PATTERN = Pattern.compile("(?<=\\()[^)]+");//匹配小括号
    /**
     * 解析查询条件
     * @param match JSON字符串查询条件
     * @param tag 节点类型
     * @return
     */
    public static String analysisMatch(String match, String tag) {
        StringBuilder conditon = new StringBuilder();
        JSONArray jsonArray = JSONUtil.parseArray(match);
        for (int i = 0; i < jsonArray.size(); i++) {
            JSONArray jsonArray2 = jsonArray.getJSONArray(i);
            conditon.append("(");
            for (int j = 0; j < jsonArray2.size(); j++) {
                JSONObject jsonObject = jsonArray2.getJSONObject(j);
                conditon.append(parseMatch(jsonObject.getStr("matchType"), jsonObject.getStr("field"),
                        jsonObject.getStr("value"), tag));

                if (j != jsonArray2.size() - 1) {
                    conditon.append(" and ");
                }
            }
            if (i != jsonArray.size() - 1) {
                conditon.append(") or ");
            } else {
                conditon.append(")");
            }
        }
//        log.info("解析后的条件为{}", conditon.toString());
        return conditon.toString();
    }

    private static String parseMatch(String matchType, String field, String value, String tag) {
        if (field.equals("id")) {
            if (matchType.equals("eq")) {
                return "id(n) == '" + value + "'";
            } else if (matchType.equals("contains"))  {
                return "id(n) CONTAINS '" + value + "'";
            }
        } else {
            if (matchType.equals("contains")) {
                return "n." + tag + "." + field + " CONTAINS '" + value + "'";
            } else if (matchType.equals("gt")) {
                return "n." + tag + "." + field + " >= " + value;
            } else if (matchType.equals("lt")) {
                return "n." + tag + "." + field + " <= " + value;
            } else if (matchType.equals("re")) {
                return "n." + tag + "." + field + " =~ " + value;
            } else if (matchType.equals("startWith")) {
                return "n." + tag + "." + field + " STARTS WITH '" + value + "'";
            } else if (matchType.equals("endWith")) {
                return "n." + tag + "." + field + " ENDS WITH '" + value + "'";
            } else if (matchType.equals("eq") && NumberUtil.isNumber(value)) {
                // 比较的值是数字，不用加''
                return "n." + tag + "." + field + " == " + value;
            } else if (matchType.equals("eq") && !NumberUtil.isNumber(value)){
                // 比较的值是字符串，需要加''
                return "n." + tag + "." + field + " == '" + value + "'";
            }
        }

        return "";
    }

    /**
     * 查询nebula graph中所有的图空间
     * @return
     */
    public static String spaceInfo() {
        String allSpace = "show spaces";
        log.info("空间信息-GQL语句：{}", allSpace);
        return allSpace;
    }

    /**
     * 统计图空间信息
     * @param spaceName 图空间名字
     * @return
     */
    public static String spaceStatsInfo(String spaceName) {
        String spaceStats = "USE " + spaceName + "; SHOW STATS";
        log.info("空间信息-GQL语句：{}", spaceStats);
        return spaceStats;
    }

    public static String getPropertyInfo(String space, String tag) {
        String spaceStats = "USE " + space + "; DESCRIBE TAG " + tag;
        log.info("空间信息-GQL语句：{}", spaceStats);
        return spaceStats;
    }

    public static String getTagIndexInfo(String space) {
        String spaceStats = "USE " + space + "; SHOW TAG INDEXES";
        log.info("空间信息-GQL语句：{}", spaceStats);
        return spaceStats;
    }


    public static String get(String space) {
        String spaceStats = "USE " + space + "; SHOW TAG INDEXES";
        log.info("空间信息-GQL语句：{}", spaceStats);
        return spaceStats;
    }

    /**
     * 查询全部实体
     * @param space 空间名称
     * @param tag 实体类型
     * @param number 要返回的个数；如果不传，默认返回全部查询到的实体
     * @return
     */
    public static String queryVertex(String space, String tag, int number) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("MATCH (v:`" + tag + "`) RETURN v limit " + number + "");
        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
        log.info("match查询-GQL语句：{}", queryMatch);
        return queryMatch;
    }

    /**
     * 询实体以及实体的边
     * @param space 查询空间
     * @param tag 实体类型
     * @param relation 是否要边：0为不要，1为要
     * @return
     */
    public static String queryVertexWithEdge(String space, String tag, int relation, String condition) {
        StringBuffer stringBuffer = new StringBuffer(); // 构建查询语句
        if (relation == 0) {
            // 不需要查询边
            stringBuffer.append("MATCH (n:`" + tag + "`) WHERE " + condition + " RETURN n");
        } else if (relation == 1) {
            // 需要查询边
            stringBuffer.append("MATCH (n:`" + tag + "`)-[e]-(v) WHERE " + condition + " RETURN n,v,e");
        }
        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
        return queryMatch;
    }

    /**
     * 查询关系
     * @param space 查询空间
     * @param relation 要查询的关系
     * @param startTag 要查询的start实体类型
     * @param endTag 要查询的end实体类型
     * @param number 要返回的个数
     * @return
     */
    public static String queryRelation(String space, String relation, String startTag, String endTag, Integer number) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("MATCH " + startTag + "-" + relation + "->" + endTag + " RETURN e LIMIT " + number + "");
        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
        return queryMatch;
    }

    /**
     * 查询关系
     * @param space 查询空间
     * @param relation 要查询的关系
     * @param startTag 要查询的start实体类型
     * @param endTag 要查询的end实体类型
     * @param number 要返回的个数
     * @return
     */
    public static String queryRelationMulti(String space, String relation, String startTag, String endTag, Integer number) {
        StringBuffer stringBuffer = new StringBuffer();
        String[] startTagArray = startTag.split(":");
        String[] endTagArray = endTag.split(":");

        for (int i = 0; i < startTagArray.length; i++) {
            if (startTagArray.length != 1 && StringUtils.isEmpty(startTagArray[i])) {
                continue;
            }
            for (int j = 0; j < endTagArray.length; j++) {
                if (endTagArray.length != 1 && StringUtils.isEmpty(endTagArray[j])) {
                    continue;
                }

                if (startTagArray.length == 1) {
                    stringBuffer.append("MATCH ()");
                } else {
                    stringBuffer.append("MATCH (:" + startTagArray[i] + ")");
                }
                stringBuffer.append("-" + relation + "->");

                if (endTagArray.length == 1) {
                    stringBuffer.append("() ");
                } else {
                    stringBuffer.append("(:" + endTagArray[j] + ") ");
                }

                if (i == (startTagArray.length - 1) && j == (endTagArray.length - 1)) {
                    stringBuffer.append("RETURN e LIMIT " + number);
                } else {
                    stringBuffer.append("RETURN e LIMIT " + number).append(NEWLINE).append( "UNION DISTINCT").append(NEWLINE);
                }
            }

        }

        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
//        log.info(queryMatch);
        return queryMatch;
    }

    /**
     * 查询实体关系类型
     * @param space 空间名称
     * @param tag 节点类型
     * @param condition 条件
     * @return
     */
    public static String queryVertexRelationType(String space, String tag, String condition) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("MATCH (n:" + tag + ")-[e]-() WHERE " + condition + " RETURN n, type(e) AS relation");
        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
//        log.info(queryMatch);
        return queryMatch;
    }

    /**
     * 导出子图
     * @param space 空间名称
     * @param condition 条件
     * @return
     */
    public static String querySubgraph(String space, String condition) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("GET SUBGRAPH WITH PROP 1 STEPS FROM " + condition + " YIELD VERTICES AS nodes, EDGES AS relationships");
        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
//        log.info(queryMatch);
        return queryMatch;
    }

    /**
     * 两个实体间的hop跳路径查询
     * @param space 空间名称
     * @param sourceId 开始节点id
     * @param targetId 结束节点id
     * @param hop 跳数
     * @param path 边的类型限制
     * @param direction 边的方向约束
     * @return
     */
    public static String queryHopPath(String space, String sourceId, String targetId, int hop, String path, int direction) {
        StringBuffer stringBuffer = new StringBuffer();
        if (direction == 0) {
            stringBuffer.append("FIND NOLOOP PATH WITH PROP FROM '" + sourceId + "' TO '" + targetId + "' OVER " + path + " UPTO " + hop + " STEPS YIELD path AS p");
        } else if (direction == 1) {
            stringBuffer.append("FIND NOLOOP PATH WITH PROP FROM '" + sourceId + "' TO '" + targetId + "' OVER " + path + " REVERSELY UPTO " + hop + " STEPS YIELD path AS p");
        } else if (direction == 2) {
            stringBuffer.append("FIND NOLOOP PATH WITH PROP FROM '" + sourceId + "' TO '" + targetId + "' OVER " + path + " BIDIRECT UPTO " + hop + " STEPS YIELD path AS p");
        }

        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
//        log.info(queryMatch);
        return queryMatch;
    }

    /**
     * 查询关系链邻居
     * @param space 空间名称
     * @param tag 节点类型
     * @param path 路径条件
     * @param condition 筛选条件
     * @return
     */
    public static String queryNeighborEntity(String space, String tag, String path, String condition) {
        StringBuilder sb = new StringBuilder(String.format("USE `%s`;", space)).append(NEWLINE);
        String rePath = path.replace("(start)", "p = (n:" + tag + ")");
        sb.append("MATCH ").append(rePath).append(NEWLINE);
        sb.append("WHERE ").append(condition).append(NEWLINE);
        sb.append("RETURN n, end, p");
        return sb.toString();
    }
    /**
     * 查询关系链邻居(多实体拼接)
     * @param space 空间名称
     * @param tag 节点类型
     * @param path 路径条件
     * @param condition 筛选条件
     * @return
     */
    public static String queryNeighborEntityByMulti(String space, String tag, String path, String condition) {
        StringBuilder sb = new StringBuilder(String.format("USE `%s`;", space)).append(NEWLINE);
        Matcher mat = PARENTHESIS_PATTERN.matcher(path);
        List<String> parenthesisContentList = Lists.newArrayList();
        while(mat.find()){
            parenthesisContentList.add(mat.group());
        }
        if(CollectionUtils.isNotEmpty(parenthesisContentList)){
            List<List<String>> arrayContentList = Lists.newArrayList();
            Iterator<String> it = parenthesisContentList.iterator();
            while (it.hasNext()){
                String itValue = it.next();
                String[] splitArray = itValue.split("\\|");
                if(splitArray.length < 2){
                    it.remove();
                    continue;
                }
                if(itValue.startsWith(":")){
                    splitArray[0] = splitArray[0].substring(1);
                }
                if(itValue.toLowerCase().startsWith("end:")){
                    splitArray[0] = splitArray[0].substring(4);
                }
                arrayContentList.add(Lists.newArrayList(splitArray));
            }
            if(CollectionUtils.isNotEmpty(arrayContentList)){
                List<List<String>> combinationsList = getCombinations(arrayContentList);
                for(int j = 0 ; j < combinationsList.size() ;j++){
                    List<String> tmpList = combinationsList.get(j);
                    String tmpPath = path;
                    for(int i = 0 ; i < parenthesisContentList.size() ;i++){
                        if(parenthesisContentList.get(i).toLowerCase().startsWith("end:")){
                            tmpPath = StringUtils.replaceOnce(tmpPath, parenthesisContentList.get(i), "end:" + tmpList.get(i));
                        }else{
                            tmpPath = StringUtils.replaceOnce(tmpPath, parenthesisContentList.get(i), ":" + tmpList.get(i));
                        }
                    }
                    String rePath = tmpPath.replace("(start)", "p = (n:" + tag + ")");
                    sb.append("MATCH ").append(rePath).append(NEWLINE);
                    sb.append("WHERE ").append(condition).append(NEWLINE);
                    sb.append("RETURN n, end, p");
                    if(j != combinationsList.size() - 1){
                        sb.append(NEWLINE).append("union").append(NEWLINE);
                    }
                }
                return sb.toString();
            }
        }
        String rePath = path.replace("(start)", "p = (n:" + tag + ")");
        sb.append("MATCH ").append(rePath).append(NEWLINE);
        sb.append("WHERE ").append(condition).append(NEWLINE);
        sb.append("RETURN n, end, p");
        return sb.toString();
    }

    /**
     * 获取排列
     * @param input
     * @return
     */
    public static List<List<String>> getCombinations(List<List<String>> input) {
        int[] idx = new int[input.size()];
        List<String> base = input.stream().map(p -> p.get(0)).collect(Collectors.toList());

        List<List<String>> allCombinations  = Lists.newArrayList();
        while(true)
        {
            allCombinations.add(new ArrayList<>(base));

            int k=idx.length-1;
            for(; k>=0; k--)
            {
                idx[k] += 1;
                if(idx[k] < input.get(k).size())
                {
                    base.set(k, input.get(k).get(idx[k]));
                    break;
                }
                idx[k] = 0;
                base.set(k, input.get(k).get(idx[k]));
            }
            if(k < 0) break;
        }
        return allCombinations;
    }
    /**
     * HOP跳查询接口
     * @param space 空间名称
     * @param startTag 开始节点类型
     * @param edgeType 边类型
     * @param endTag 结束节点类型
     * @param condition 解析后的条件
     * @param hop 跳数
     * @param direction 关系方向约束
     * @return
     */
    public static String queryHopEntity(String space, String startTag, String edgeType, String endTag, String condition, int hop, int direction) {
        StringBuffer stringBuffer = new StringBuffer();
        if (direction == 0) {
            stringBuffer.append("MATCH p=(n:" + startTag + ")-[" + edgeType + "*" + hop + "]->(" + endTag + ") WHERE " + condition + " RETURN p");
        } else if (direction == 1) {
            stringBuffer.append("MATCH p=(n:" + startTag + ")<-[" + edgeType + "*" + hop + "]-(" + endTag + ") WHERE " + condition + " RETURN p");
        } else if (direction == 2) {
            stringBuffer.append("MATCH p=(n:" + startTag + ")-[" + edgeType + "*" + hop + "]-(" + endTag + ") WHERE " + condition + " RETURN p");
        }

        String queryMatch = String.format("USE `%s`; " + stringBuffer.toString() + "", space);
        log.info(queryMatch);
        return queryMatch;
    }

    /**
     * HOP跳查询接口
     * @param space 空间名称
     * @param startTag 开始节点类型
     * @param edgeType 边类型
     * @param endTag 结束节点类型
     * @param condition 解析后的条件
     * @param hop 跳数
     * @param direction 关系方向约束
     * @return
     */
    public static String queryHopEntityMulti(String space, String startTag, String edgeType, String endTag, String condition, int hop, int direction) {
        StringBuilder stringBuilder = new StringBuilder();
        if(endTag.toLowerCase().startsWith("v:")){
            endTag = endTag.substring(2);
        }
        String[] splitArray = endTag.split("\\|");
        int i = 0;
        for(String str : splitArray){
            String tmpEndTag = StringUtils.isEmpty(str) ? "" : "v:" + str;
            if (direction == 0) {
                stringBuilder.append("MATCH p=(n:" + startTag + ")-[" + edgeType + "*" + hop + "]->(" + tmpEndTag + ") WHERE " + condition + " RETURN p");
            } else if (direction == 1) {
                stringBuilder.append("MATCH p=(n:" + startTag + ")<-[" + edgeType + "*" + hop + "]-(" + tmpEndTag + ") WHERE " + condition + " RETURN p");
            } else if (direction == 2) {
                stringBuilder.append("MATCH p=(n:" + startTag + ")-[" + edgeType + "*" + hop + "]-(" + tmpEndTag + ") WHERE " + condition + " RETURN p");
            }
            if(i != splitArray.length - 1){
                stringBuilder.append(NEWLINE).append("union").append(NEWLINE);
            }
            ++i;
        }

        String queryMatch = String.format("USE `%s`; " + stringBuilder.toString() + "", space);
//        log.info(queryMatch);
        return queryMatch;
    }

}
